import {
  Controller,
  Post,
  Get,
  Body,
  Param,
  Query,
  UseGuards,
  HttpException,
  HttpStatus,
  HttpCode,
  Logger,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../../../../modules/user/jwt/jwt-auth.guard';
import { CurrentUser } from 'src/decorators/current.user';
import { UserEntity } from 'src/types/modules/user/entities/user.entity';
import { CurrentRole } from '@/decorators/current.role';
import { ClientRole } from '@/modules/user/role/role.type';
import { VFSMCPServer } from './vfs-mcp-server';
import {
  MCPMessage,
  MCPResponse,
  MCPResourceContent,
  MCPToolResult,
  MCPPromptResult,
  MCPInitializeParams,
  MCPInitializeResult,
  MCP_ERROR_CODES,
  ProjectContext,
} from './types';

@ApiTags('MCP - Model Context Protocol')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('editor/mcp')
export class MCPController {
  private readonly logger = new Logger(MCPController.name);

  constructor(private readonly mcpServer: VFSMCPServer) {}

  /**
   * MCP协议初始化握手
   * 客户端连接时首先调用此接口进行协议协商
   */
  @Post('initialize')
  @HttpCode(HttpStatus.OK)
  async initialize(@Body() params: MCPInitializeParams): Promise<MCPResponse<MCPInitializeResult>> {
    try {
      this.logger.log(`MCP Initialize from client: ${params.clientInfo.name} v${params.clientInfo.version}`);

      // 返回服务器能力和信息
      const result: MCPInitializeResult = {
        protocolVersion: '2024-11-05',
        capabilities: {
          resources: {
            list: true,
            read: true,
            subscribe: false, // 暂不支持实时订阅
          },
          tools: {
            list: true,
            call: true,
          },
          prompts: {
            list: true,
            get: true,
          },
          logging: {
            level: 'info',
          },
        },
        serverInfo: {
          name: 'Low-Code Platform VFS MCP Server',
          version: '1.0.0',
        },
      };

      return {
        success: true,
        data: result,
        _meta: {
          requestId: this.generateRequestId(),
          timestamp: Date.now(),
        },
      };
    } catch (error) {
      this.logger.error('MCP Initialize failed', error.stack);
      return {
        success: false,
        error: {
          code: MCP_ERROR_CODES.INTERNAL_ERROR,
          message: 'MCP initialization failed',
        },
      };
    }
  }

  /**
   * 列出所有可用资源
   */
  @Get('resources/list')
  @ApiOperation({ summary: 'List all available resources' })
  async listResources(
    @Query('projectId') projectId?: string,
    @CurrentUser() user?: UserEntity,
    @CurrentRole() role?: ClientRole,
  ): Promise<MCPResponse<any[]>> {
    const context: ProjectContext = {
      projectId: projectId || '',
      projectName: 'Unknown',
      userId: user?.id,
      userName: user?.name,
      permissions: [], // TODO: 从role中获取权限列表
      environment: 'development',
    };

    return this.mcpServer.listResources(projectId);
  }

  /**
   * 读取指定资源内容
   */
  @Post('resources/read')
  @ApiOperation({ summary: 'Read resource content' })
  async readResource(
    @Body() body: { uri: string; projectId?: string },
    @CurrentUser() user: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<MCPResponse<MCPResourceContent>> {
    const context: ProjectContext = {
      projectId: body.projectId || this.extractProjectIdFromURI(body.uri),
      projectName: 'Unknown',
      userId: user.id,
      userName: user.name,
      permissions: [], // TODO: 从role中获取权限列表
      environment: 'development',
    };

    return this.mcpServer.readResource(body.uri, context);
  }

  /**
   * 列出所有可用工具
   */
  @Get('tools/list')
  @ApiOperation({ summary: 'List all available tools' })
  async listTools(): Promise<MCPResponse<any[]>> {
    return this.mcpServer.listTools();
  }

  /**
   * 调用指定工具
   */
  @Post('tools/call')
  @ApiOperation({ summary: 'Execute a tool' })
  async callTool(
    @Body() body: { name: string; arguments?: Record<string, any>; projectId?: string },
    @CurrentUser() user: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<MCPResponse<MCPToolResult>> {
    const context: ProjectContext = {
      projectId: body.projectId || (body.arguments?.projectId),
      projectName: 'Unknown',
      userId: user.id,
      userName: user.name,
      permissions: [], // TODO: 从role中获取权限列表
      environment: 'development',
    };

    return this.mcpServer.callTool(body.name, body.arguments || {}, context);
  }

  /**
   * 列出所有可用提示模板
   */
  @Get('prompts/list')
  @ApiOperation({ summary: 'List all available prompts' })
  async listPrompts(): Promise<MCPResponse<any[]>> {
    return this.mcpServer.listPrompts();
  }

  /**
   * 获取提示模板内容
   */
  @Post('prompts/get')
  @ApiOperation({ summary: 'Get prompt template' })
  async getPrompt(
    @Body() body: { name: string; arguments?: Record<string, any> },
  ): Promise<MCPResponse<MCPPromptResult>> {
    return this.mcpServer.getPrompt(body.name, body.arguments);
  }

  /**
   * 通用MCP消息处理接口
   * 支持标准MCP JSON-RPC协议
   */
  @Post('message')
  @ApiOperation({ summary: 'Handle MCP JSON-RPC message' })
  async handleMessage(@Body() message: MCPMessage): Promise<MCPMessage> {
    try {
      this.logger.debug(`Received MCP message: ${message.method}`, message.params);

      let result: any;
      let error: any;

      switch (message.method) {
        case 'initialize':
          result = await this.initialize(message.params as MCPInitializeParams);
          break;

        case 'resources/list':
          result = await this.listResources(message.params?.projectId);
          break;

        case 'resources/read':
          result = await this.readResource(message.params, {} as UserEntity, {} as ClientRole);
          break;

        case 'tools/list':
          result = await this.listTools();
          break;

        case 'tools/call':
          result = await this.callTool(message.params, {} as UserEntity, {} as ClientRole);
          break;

        case 'prompts/list':
          result = await this.listPrompts();
          break;

        case 'prompts/get':
          result = await this.getPrompt(message.params);
          break;

        default:
          throw new HttpException(
            `Method not found: ${message.method}`,
            HttpStatus.NOT_FOUND,
          );
      }

      return {
        id: message.id,
        jsonrpc: '2.0',
        result: result.success ? result.data : { error: result.error },
      };
    } catch (error) {
      this.logger.error(`MCP message processing failed`, error.stack);

      return {
        id: message.id,
        jsonrpc: '2.0',
        error: {
          code: this.mapErrorToMCPCode(error),
          message: error.message,
        },
      };
    }
  }

  /**
   * 批量资源操作
   * 扩展MCP协议，支持批量操作以提高性能
   */
  @Post('batch')
  @ApiOperation({ summary: 'Batch operations on multiple resources' })
  async batchOperations(
    @Body()
    body: {
      operations: Array<{
        type: 'resource' | 'tool';
        method: string;
        params: any;
      }>;
      projectId?: string;
      continueOnError?: boolean;
    },
    @CurrentUser() user: UserEntity,
    @CurrentRole() role: ClientRole,
  ): Promise<MCPResponse<any[]>> {
    const context: ProjectContext = {
      projectId: body.projectId || '',
      projectName: 'Unknown',
      userId: user.id,
      userName: user.name,
      permissions: [], // TODO: 从role中获取权限列表
      environment: 'development',
    };

    const results = [];
    const { operations, continueOnError = true } = body;

    for (const op of operations) {
      try {
        let result: any;

        if (op.type === 'resource') {
          switch (op.method) {
            case 'read':
              result = await this.mcpServer.readResource(op.params.uri, context);
              break;
            default:
              throw new Error(`Unsupported resource operation: ${op.method}`);
          }
        } else if (op.type === 'tool') {
          result = await this.mcpServer.callTool(op.method, op.params, context);
        } else {
          throw new Error(`Unsupported operation type: ${op.type}`);
        }

        results.push({
          operation: op,
          success: true,
          data: result.success ? result.data : null,
          error: result.success ? null : result.error,
        });
      } catch (error) {
        results.push({
          operation: op,
          success: false,
          error: {
            code: this.mapErrorToMCPCode(error),
            message: error.message,
          },
        });

        if (!continueOnError) {
          throw error;
        }
      }
    }

    return {
      success: true,
      data: results,
      _meta: {
        requestId: this.generateRequestId(),
        timestamp: Date.now(),
      },
    };
  }

  /**
   * 健康检查接口
   */
  @Get('health')
  @ApiOperation({ summary: 'MCP Server health check' })
  async healthCheck(): Promise<MCPResponse<{ status: string; version: string }>> {
    return {
      success: true,
      data: {
        status: 'healthy',
        version: '1.0.0',
      },
      _meta: {
        requestId: this.generateRequestId(),
        timestamp: Date.now(),
      },
    };
  }

  /**
   * 服务器信息接口
   */
  @Get('info')
  @ApiOperation({ summary: 'MCP Server information' })
  async getServerInfo(): Promise<MCPResponse<any>> {
    return {
      success: true,
      data: {
        name: 'Low-Code Platform VFS MCP Server',
        version: '1.0.0',
        protocolVersion: '2024-11-05',
        capabilities: {
          resources: ['list', 'read'],
          tools: ['list', 'call'],
          prompts: ['list', 'get'],
        },
        features: [
          'Virtual File System',
          'Code Generation',
          'File Validation',
          'Batch Operations',
          'Project Management',
        ],
      },
      _meta: {
        requestId: this.generateRequestId(),
        timestamp: Date.now(),
      },
    };
  }

  // ===== 私有辅助方法 =====

  private generateRequestId(): string {
    return `mcp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  private extractProjectIdFromURI(uri: string): string {
    const match = uri.match(/^project:\/\/([^\/]+)\//);
    return match ? match[1] : '';
  }

  private mapErrorToMCPCode(error: any): number {
    if (error.status === HttpStatus.NOT_FOUND) return MCP_ERROR_CODES.RESOURCE_NOT_FOUND;
    if (error.status === HttpStatus.FORBIDDEN) return MCP_ERROR_CODES.RESOURCE_ACCESS_DENIED;
    if (error.status === HttpStatus.BAD_REQUEST) return MCP_ERROR_CODES.INVALID_PARAMS;
    if (error.status === HttpStatus.NOT_FOUND) return MCP_ERROR_CODES.METHOD_NOT_FOUND;
    return MCP_ERROR_CODES.INTERNAL_ERROR;
  }
}